try {
	Tests.blah
} catch (e) {
	Tests = {};
}

var DBTests = new Class({
	setup: function () {
		this.oldDataPath = Dao.dataPath;
		Dao.dataPath = system.widgetDataFolder + "/test.db";
		this.deleteDb();
	},
		
	deleteDb: function () {
		if (filesystem.itemExists(Dao.dataPath)) {
			filesystem.moveToRecycleBin(Dao.dataPath);
		}
	},
	
	teardown: function () {
		Dao.close();
		this.deleteDb();
		Dao.dataPath = this.oldDataPath;
		Entity.Entities = [];
		Dao.clearEntityCache();
	}
});

Tests.DaoTests = DBTests.extend({
	setup: function () {
		this.parent();
	
		Tests.SimpleTestEntity = Entity.extend('test', 
			{
				name: String
			},
			{});
		Tests.SimpleTestEntity.setConstraint('name', CONSTRAINT_NOT_NULL);
		this.testEntity1 = Tests.SimpleTestEntity.create({name: 'one'});
		this.testEntity2 = Tests.SimpleTestEntity.create({name: 'two'});
	},
	
	teardown: function () {
		this.parent();
		Tests.SimpleTestEntity = null;
	},
	
	testGetInstance: function () {
		assertNotNull(Dao.instance);
	},
	
	testGetMaxId: function () {
		assertEquals(3, Dao.getMaxId(Tests.SimpleTestEntity.table));		
		this.testEntity1.delete();
		this.testEntity2.delete();
		assertEquals(1, Dao.getMaxId(Tests.SimpleTestEntity.table));
	}, 
	
	testGetSchema: function () {
		assertEquals("CREATE TABLE IF NOT EXISTS `test` ( `id` INTEGER PRIMARY KEY, `version` INTEGER NOT NULL , `name` varchar(255) NOT NULL);\n", 
			Dao.schema);
	},
	
	testGetEntity: function () {
		assertNotEquals(0, this.testEntity1.id);
		var testEntity1Copy = Dao.getEntity(Tests.SimpleTestEntity, this.testEntity1.id);
		assertEquals(this.testEntity1.id, testEntity1Copy.id);
		assertEquals(this.testEntity1.name, testEntity1Copy.name);
		
		assertNull(Dao.getEntity(Tests.SimpleTestEntity, 3));
	},
	
	testSaveEntity: function () {
		var testEntity = new Tests.SimpleTestEntity({name: 'three'});
		Dao.saveOrUpdate(testEntity);
		assertEquals(testEntity.name, Dao.getEntity(Tests.SimpleTestEntity, testEntity.id).name);
	},
	
	testUpdateEntity: function () {
		this.testEntity1.name = 'numerical';
		Dao.saveOrUpdate(this.testEntity1);
		assertEquals(this.testEntity1.name, Dao.getEntity(Tests.SimpleTestEntity, this.testEntity1.id).name);
	},
	
	testDeleteEntity: function () {
		assertTrue(Dao.delete(this.testEntity1));		
		assertNull(Dao.getEntity(Tests.SimpleTestEntity, this.testEntity1.id));
		
		assertNotEquals(0, this.testEntity1.id);
		assertFalse(Dao.delete(this.testEntity1));
		
		this.testEntity1.id = 0;
		assertFalse(this.testEntity1.isSaved());		
		assertFalse(Dao.delete(this.testEntity1));
	},
	
	testGetEntitiesByFieldsValueSingleReturnValue: function () {
		this.testEntity1.id = this.testEntity1.version = 0;
		assertNotEquals(this.testEntity1.name, this.testEntity2.name);
		var rslts = Dao.getEntitiesByFieldsValue(this.testEntity1);
		assertEquals(1, rslts.length);
		assertEquals(this.testEntity1.name, rslts[0].name);
	}
}); 

Tests.EntityClassTests = DBTests.extend({
	setup: function () {
		this.parent();
		Tests.SimpleTestEntity = Entity.extend('test', 
			{
				name: String
			},
		{});
		Tests.SimpleTestEntity.setConstraint('name', CONSTRAINT_NOT_NULL);
		
		Tests.AnotherSimpleTestEntity = Entity.extend('test_two',
			{
				data: Tests.SimpleTestEntity				
			},
		{});
		Tests.AnotherSimpleTestEntity.setConstraint('data', CONSTRAINT_UNIQUE);
		
		this.testEntity1 = Tests.AnotherSimpleTestEntity.create({
				data: Tests.SimpleTestEntity.create({
					name: 'one'
				})
		});
		this.testEntity2 = Tests.AnotherSimpleTestEntity.create({data: null});
	},
	
	teardown: function () {
		this.parent();
		Tests.SimpleTestEntity = null;
		Tests.AnotherSimpleTestEntity = null;
	},

	testSchema: function () {
		var tableEquals = function (klazz) {
			return klazz.table == this.table;
		};
	
		assertEquals("test", Tests.SimpleTestEntity.table);
		assertTrue(Entity.Entities.some(tableEquals.bind(Tests.SimpleTestEntity)));
		assertNotNull(Tests.SimpleTestEntity.fieldClasses);
		assertEquals("CREATE TABLE IF NOT EXISTS `test` ( `id` INTEGER PRIMARY KEY, `version` INTEGER NOT NULL , `name` varchar(255) NOT NULL);", 
			Tests.SimpleTestEntity.schema());
		assertEquals(Tests.SimpleTestEntity, this.testEntity1.data.class);
		
		assertEquals("test_two", Tests.AnotherSimpleTestEntity.table);
		assertTrue(Entity.Entities.some(tableEquals.bind(Tests.AnotherSimpleTestEntity)));
		assertNotNull(Tests.AnotherSimpleTestEntity.fieldClasses);
		assertEquals("CREATE TABLE IF NOT EXISTS `test_two` ( `id` INTEGER PRIMARY KEY, `version` INTEGER NOT NULL , `data_id` INTEGER  CONSTRAINT `fk_test_two_test` REFERENCES `test`(`id`), UNIQUE(data_id));", 
			Tests.AnotherSimpleTestEntity.schema());
		assertEquals(Tests.AnotherSimpleTestEntity, this.testEntity1.class);
	},
	
	testFieldNotNull: function () {
		assertFalse(Tests.AnotherSimpleTestEntity.fieldNotNull('data'));
		assertTrue(Tests.SimpleTestEntity.fieldNotNull('name'));
	},
	
	testFieldUnique: function () {
		assertFalse(Tests.SimpleTestEntity.fieldUnique('name'));
		assertTrue(Tests.AnotherSimpleTestEntity.fieldUnique('data'));
	},
	
	testGetEntity: function () {
		var testEntity1Copy = Tests.AnotherSimpleTestEntity.getEntity(this.testEntity1.id);
		assertEquals(this.testEntity1.data.name, testEntity1Copy.data.name);
		var testEntity2Copy = Tests.AnotherSimpleTestEntity.getEntity(this.testEntity2.id);
		assertNull(testEntity2Copy.data);
	},
	
	testGetAllEntity: function () {
		var allAnotherSimpleTestEntity = Tests.AnotherSimpleTestEntity.getAllEntities();
		assertEquals(2, allAnotherSimpleTestEntity.length);
		assertEquals(this.testEntity1.id, allAnotherSimpleTestEntity[0].id);
		assertEquals(this.testEntity2.id, allAnotherSimpleTestEntity[1].id);
		
		var testEntity3 = Tests.AnotherSimpleTestEntity.create({
				data: Tests.SimpleTestEntity.create({
					name: 'two'
				})
		});
		allAnotherSimpleTestEntity = Tests.AnotherSimpleTestEntity.getAllEntities();
		assertEquals(3, allAnotherSimpleTestEntity.length);
		assertEquals(testEntity3.id, allAnotherSimpleTestEntity[2].id);
	},
	
	testGetAllEntityWithCriteria: function () {
		log(this.testEntity1.data.id);
		var someAnotherSimpleTestEntity = Tests.AnotherSimpleTestEntity.getAllEntities("data_id={0}".substitute([this.testEntity1.data.id]));
		assertEquals(1, someAnotherSimpleTestEntity.length);
		assertEquals(this.testEntity1.id, someAnotherSimpleTestEntity[0].id);
	},
	
	testCreate: function () {
		var testEntity3 = Tests.AnotherSimpleTestEntity.create({
				data: Tests.SimpleTestEntity.create({
					name: 'two'
				})
		});
		assertTrue(testEntity3.isSaved());
		assertTrue(testEntity3.equals(Tests.AnotherSimpleTestEntity.getEntity(testEntity3.id)));
	},
	
	testMultiCreate: function () {
		var testEntity3, testEntity4;
		[testEntity3, testEntity4] = Tests.AnotherSimpleTestEntity.create({
				data: Tests.SimpleTestEntity.create({
					name: 'two'
				})
		}, {
				data: Tests.SimpleTestEntity.create({
					name: 'three'
				})
		});
		assertTrue(testEntity3.isSaved());
		assertTrue(testEntity4.isSaved());
		assertTrue(testEntity3.equals(Tests.AnotherSimpleTestEntity.getEntity(testEntity3.id)));
		assertTrue(testEntity4.equals(Tests.AnotherSimpleTestEntity.getEntity(testEntity4.id)));
	},
	
	testCreateFailed: function () {
		try {
			Tests.SimpleTestEntity.create({ name: null});
			fail("should not be able to create.");
		} catch (e) {
			assertEquals(1, 
				fetchResult(Dao.instance.query(
					"SELECT count(*) FROM `?`;".format(Tests.SimpleTestEntity.table))));
			Dao.instance.close();
		}
	},
	
	testFind: function () {
		var existingSimpleEntity = this.testEntity1.data;
		var simpleEntity1 = Tests.SimpleTestEntity.create({ name: existingSimpleEntity.name });
		var simpleEntity2 = Tests.SimpleTestEntity.create({ name: 'two' });
		
		// returns 2
		assertEquals(existingSimpleEntity.name, simpleEntity1.name);
		const idEqual = function (item) { return item.id == this.id; }
		var rslt = Tests.SimpleTestEntity.find({ name: simpleEntity1.name });
		assertEquals(2, rslt.length);
		assertTrue(rslt.find(idEqual.bind(simpleEntity1)) >= 0);
		assertTrue(rslt.find(idEqual.bind(existingSimpleEntity)) >= 0);
		
		// return 1
		rslt = Tests.SimpleTestEntity.find({ name: simpleEntity2.name });
		assertEquals(simpleEntity2.id, rslt[0].id);
		
		// return none
		assertEquals(0, 
			Tests.SimpleTestEntity.find({ name: 'bogus name' }).length);
		
		//  invalid field
		assertEquals(0, Tests.SimpleTestEntity.find({ bogus: 'value' }).length);
	},
	
	testCreateOrFind: function () {
		var existingSimpleEntity = this.testEntity1.data;
		var simpleEntity1 = Tests.SimpleTestEntity.createOrFind({ name: existingSimpleEntity.name });
		var simpleEntity2 = Tests.SimpleTestEntity.createOrFind({ name: 'two' });

		assertEquals(existingSimpleEntity.id, simpleEntity1.id);
		assertTrue(simpleEntity2.isSaved());
		assertEquals("should have 2 as simpleEntity1 == existingSimpleEntity", 2, 
				fetchResult(Dao.instance.query(
					"SELECT count(*) FROM `?`;".format(Tests.SimpleTestEntity.table))));
					
		// @TODO write tests for find duplicate entities
	},
	
	testHasOne: function () {
		Tests.SimpleTestEntity.hasOne('related', Tests.AnotherSimpleTestEntity, 'data');
		assertEquals(this.testEntity1.id, this.testEntity1.data.related.id);
	},
	
	// @TODO test hasMany
	
	testSaveUpdateDeleteMethods: function () {
		const nameValue1 = 'testing';
		var entity = new Tests.AnotherSimpleTestEntity({
			data: new Tests.SimpleTestEntity({
				name: nameValue1
			})
		});
		
		assertFalse('is not saved', entity.isSaved());
		entity.saveOrUpdate();
		assertTrue('should now be saved', entity.isSaved());
		
		var dbEntity = Tests.AnotherSimpleTestEntity.getEntity(entity.id);
		assertFalse('should not be same instance', dbEntity === entity);
		assertEquals(dbEntity.id, entity.id);
		assertEquals(dbEntity.data.id, entity.data.id);
		assertEquals(nameValue1, dbEntity.data.name);
		
		const nameValue2 = 'testing two';
		entity.data.name = nameValue2;
		
		entity.saveOrUpdate();
		dbEntity = Tests.AnotherSimpleTestEntity.getEntity(entity.id);
		assertEquals('value unchanged as cascade update is not set to true', 
			nameValue1, dbEntity.data.name);
		
		entity.saveOrUpdate(true);
		dbEntity = Tests.AnotherSimpleTestEntity.getEntity(entity.id);
		assertEquals(entity.data.name, dbEntity.data.name);
		assertEquals(nameValue2, dbEntity.data.name);
		
		entity.delete();
		assertNull(Tests.AnotherSimpleTestEntity.getEntity(entity.id));
	}
	
});

